/*
 * Copyright 2008-2012 Centro Andaluz de Innovación y Tecnologías de la
 * Información y las Comunicaciones (CITIC). All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 *
 * Please contact CITIC - Edificio CITIC, C/ Marie Curie, 6 Parque
 * Tecnológico de Andalucía 29590 - Campanillas, Málaga, Spain
 * or visit www.citic.es, if you need additional information or
 * have any questions.
 */


package es.citic.puedes.youtube.umm.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.persistence.EntityManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import es.citic.puedes.youtube.IYouTubeUserModel;
import es.citic.puedes.youtube.Preference;
import es.citic.puedes.youtube.YouTubeException;
import es.citic.puedes.youtube.bbdd.YouTubeDataBase;
import es.citic.puedes.youtube.tables.Keyword;
import es.citic.puedes.youtube.tables.Userkeyword;
import es.citic.puedes.youtube.tables.YoutubeUser;

/**
 * class that implements the IYouTubeUserModel interface
 * 
 */
public class YoutubeUserModelImpl implements IYouTubeUserModel {

	/**
	 * represents the current preference model of the user
	 */
	private Map<String, Userkeyword> userKeywords = new HashMap<String, Userkeyword>();

	/**
	 * id of platform user
	 */
	private Integer userId;

	/**
	 * Log instance
	 */
	private final static Logger logger = LoggerFactory.getLogger(YoutubeUserModelImpl.class);

	private YouTubeDataBase youTubeDB;

	/**
	 * @param userId
	 * @throws YouTubeException
	 * 
	 */
	public YoutubeUserModelImpl(YouTubeDataBase youTubeDB, int userId) throws YouTubeException {

		//set bbdd params
		this.youTubeDB = youTubeDB;
		
		// sets the PUEDeS user id
		this.userId = userId;

		// gets the entity manager
		EntityManager em = youTubeDB.getEntityManager();

		// checks if the user exists in the BBDD
		YoutubeUser youTubeUser = youTubeDB.selectYouTubeUserQuery(em, userId);

		// if exists..
		if (youTubeUser != null) {

			logger.debug("user exists in PUEDeS");

			// checks if the preference model was stored previously
			List<Userkeyword> userKeywordList = youTubeDB
					.selectUserKeywordQuery(em, userId);

			// if not, creates a new preference model for that user
			if (userKeywordList.isEmpty()) {

				logger.debug("creating the model is needed");
				createNewPreferenceModel(youTubeUser);

				// if exists, loads the data from the BBDD
			} else {
				logger.debug("the model exists");
				SetCurrentPreferenceModel(userKeywordList, youTubeUser);
			}

		} else {
			throw new YouTubeException(
					"that user has not been registered on YouTubeManager service yet");
		}

		// closes the entity manager
		youTubeDB.closeEntityManager(em);
	}

	/**
	 * sets the current preference model of the YouTube user
	 * 
	 * @param userKeywordList
	 *            Userkeyword object list with the relation between the YouTube
	 *            user and keywords
	 * @param youTubeUser
	 *            YouTube user to set the preference model
	 */
	private void SetCurrentPreferenceModel(List<Userkeyword> userKeywordList,
			YoutubeUser youTubeUser) {

		logger.debug("SetCurrentPreferenceModel");

		// gets the iterator of the userKeyword object list
		Iterator<Userkeyword> userKeywordListIterator = userKeywordList
				.iterator();

		// gets the Userkeyword objects from the list and adds them to the model
		Userkeyword currentUserKeyword = null;
		while (userKeywordListIterator.hasNext()) {

			currentUserKeyword = userKeywordListIterator.next();
			userKeywords.put(currentUserKeyword.getKeyword().getKeywordname(),
					currentUserKeyword);

		}
		// printPreferences();

	}

	/**
	 * creates a new preference model for the YouTube user
	 * 
	 * @param youTubeUser
	 *            YouTube user
	 */
	private void createNewPreferenceModel(YoutubeUser youTubeUser) {

		// initializes the model
		logger.debug("initializes the model");
		userKeywords = new HashMap<String, Userkeyword>();

	}

	@Override
	public List<Preference> getKeywordPreference() {

		logger.debug("getKeywordPreference..");		

		// preference list in ascending order to return
		List<Preference> sortedPreferenceList = new ArrayList<Preference>();
		// set for sorting the preferences of the user
		SortedSet<Preference> normalizedPreferenceSet = new TreeSet<Preference>();

		// gets the set of the keys of the model
		Set<String> userKeywordKeySet = userKeywords.keySet();

		// and gets its iterator
		Iterator<String> userKeywordKeySetIterator = userKeywordKeySet
				.iterator();

		Userkeyword currentUserKeyword = null;
		Preference currentPreference = null;

		while (userKeywordKeySetIterator.hasNext()) {

			// gets the current userkeyword
			currentUserKeyword = userKeywords.get(userKeywordKeySetIterator
					.next());

			// calculates the sum of the preference counts
			Float value = new Float(currentUserKeyword.getLikecount()
					+ currentUserKeyword.getVisitcount()
					- currentUserKeyword.getDislikecount());

			// creates the new Preference object with the new value
			currentPreference = new Preference(currentUserKeyword.getKeyword()
					.getKeywordname(), value);

			// adds the current preference to a list
			if (!sortedPreferenceList.add(currentPreference)) {

				logger.debug("the preference could not be added");
			}
		}

		// normalizes the list of preferences and adds to a sorted set
		normalizedPreferenceSet = normalizeList(sortedPreferenceList);

		// sorts the normalized set in descending order and adds to a list
		sortedPreferenceList = sortNormalizedSet(normalizedPreferenceSet);

		normalizedPreferenceSet = null;

		return sortedPreferenceList;
	}

	/**
	 * sorts in ascending order the elements from a Preference object set
	 * 
	 * @param normalizePreferenceSet
	 *            a Preference object set for sorting in ascending order
	 * @return a Preference object list in descending order
	 */
	private List<Preference> sortNormalizedSet(
			SortedSet<Preference> normalizePreferenceSet) {

		// list sorted in ascending order to return
		List<Preference> orderedList = new ArrayList<Preference>();
		// list for looping through the preferences to order
		List<Preference> temporalList = new ArrayList<Preference>(
				normalizePreferenceSet);

		Preference currentPreference = null;

		if (!temporalList.isEmpty()) {
			int totalEntries = temporalList.size();

			// loops through the list in descending order to add the highest
			// elements at the beginning
			for (int i = totalEntries - 1; i >= 0; i--) {
				currentPreference = temporalList.get(i);
				orderedList.add(currentPreference);
			}
		}

		temporalList = null;

		return orderedList;
	}

	/**
	 * normalizes the count values for the preferences of an user to sort
	 * 
	 * @param PreferenceListToNormalize
	 *            Preference object list to normalize
	 * @return a Preference object set in ascending order
	 */
	private SortedSet<Preference> normalizeList(
			List<Preference> PreferenceListToNormalize) {

		// normalized sorted set of Preference objects to return
		SortedSet<Preference> normalizedPreferenceSet = new TreeSet<Preference>();

		if (!PreferenceListToNormalize.isEmpty()) {

			int totalEntries = PreferenceListToNormalize.size();
			Float currentPreferenceCount = 0F;
			Float currentPreferenceValue = 0F;
			Float currentTotalRestCount = 0F;
			Preference currentPreference = null;

			for (int i = 0; i < totalEntries; i++) {
				currentTotalRestCount = 0F;

				// gets the value of the current preference
				currentPreferenceCount = PreferenceListToNormalize.get(i)
						.getValue();

				// calculates the sum of the preferences
				for (int j = 0; j < totalEntries; j++) {

					currentTotalRestCount = currentTotalRestCount
							+ PreferenceListToNormalize.get(j).getValue();

				}

				// calculates the normalized value for the current preference
				// using the sum of preferences
				currentTotalRestCount = Math.abs(currentTotalRestCount);
				currentTotalRestCount = currentTotalRestCount / 100;
				currentPreferenceValue = currentPreferenceCount
						/ currentTotalRestCount;

				// creates and adds the normalized preference to a set
				currentPreference = new Preference(PreferenceListToNormalize
						.get(i).getName(), currentPreferenceValue);
				normalizedPreferenceSet.add(currentPreference);

			}

		}

		return normalizedPreferenceSet;
	}

	@Override
	public void saveUserModel() throws YouTubeException {

		logger.debug("saveUserModel..");

		// gets the set of the keys of the model
		Set<String> userKeywordKeySet = userKeywords.keySet();

		if (userKeywordKeySet != null) {

			// and gets its iterator
			Iterator<String> userKeywordKeySetIterator = userKeywordKeySet
					.iterator();

			// gets the entity manager
			EntityManager em = youTubeDB.getEntityManager();

			logger.debug("saveUserModel and the current model is..");
			// printPreferences();

			// begins the transaction
			em.getTransaction().begin();
			// adds the Userkeyword objects from the list to the BBDD
			Userkeyword currentUserKeyword = null;
			while (userKeywordKeySetIterator.hasNext()) {

				currentUserKeyword = userKeywords.get(userKeywordKeySetIterator
						.next());

				logger.debug("userkeywordid before persist "
						+ currentUserKeyword.getId());

				logger.debug("keywordid before persist "
						+ currentUserKeyword.getKeyword().getKeywordid());

				// creates or updates the record in the BBDD
				logger.debug("persist or merge userKeyword");
				em.merge(currentUserKeyword);

			}

			// ends the transaction
			try {
				em.getTransaction().commit();
				logger.debug("preferences of user "+userId+" updated");

			} catch (Exception e) {

				em.getTransaction().rollback();
				throw new YouTubeException(
						"the user preferences could not be updated: "
								+ e.getMessage());
			}

			youTubeDB.closeEntityManager(em);

		}

	}

	@Override
	public void setKeywordEvaluation(List<String> keywordsList, boolean hasLiked)
			throws YouTubeException {

		logger.debug("setKeywordEvaluation..");

		// checks whether the keywords exists on the BBDD and stores the new
		// keywords
		// checkKeywordExists(keywordsList);

		// keyword to check for the user
		String keywordName = null;
		// keyword counts for the user if exists
		Userkeyword currentUserKeyword = null;
		// like count to update
		Integer currentLikeCount = null;
		// dislike count to update
		Integer currentDislikeCount = null;

		if (keywordsList != null) {

			// gets the EntityManager for the BBDD queries
			EntityManager em = youTubeDB.getEntityManager();

			// gets the iterator from the keyword list
			Iterator<String> keywordsListIterator = keywordsList.iterator();

			while (keywordsListIterator.hasNext()) {

				// gets the current keyword
				keywordName = keywordsListIterator.next();

				// if an entry exists in the model for this keyword..
				if (userKeywords.containsKey(keywordName)) {

					logger.debug("keyword entry exists");

					// gets the userkeyword for this keyword..
					currentUserKeyword = userKeywords.get(keywordName);

					if (hasLiked) {
						// and updates the likecount
						currentLikeCount = currentUserKeyword.getLikecount();
						currentUserKeyword.setLikecount(currentLikeCount + 1);
					} else {
						// or updates the dislikecount
						currentDislikeCount = currentUserKeyword
								.getDislikecount();
						currentUserKeyword
						.setDislikecount(currentDislikeCount + 1);
					}

					// updates the entry
					userKeywords.put(keywordName, currentUserKeyword);

					// if not, creates a new entry for the keyword..
				} else {

					Keyword keyword = new Keyword();
					// YoutubeUser youtubeUser = new YoutubeUser();

					// gets the Keyword object for this keyword from the BBDD
					keyword = youTubeDB.selectKeywordQuery(em,
							keywordName);

					// if the keyword not exist yet..
					if (keyword == null) {
						// creates the new object with the keyword itself
						keyword = new Keyword(keywordName);
					}

					logger.debug("creates the entry for the keyword");

					// creates the new UserKeyword object with keyword,
					// youtubeuser..
					if (hasLiked) {
						// and sets the value of 'likecount' to 1
						currentUserKeyword = new Userkeyword(0, 1, 0, keyword,
								userId);
					} else {
						// or sets the value of 'dislikecount' to 1
						currentUserKeyword = new Userkeyword(1, 0, 0, keyword,
								userId);
					}

					// update the model for storing it on the BBDD later
					userKeywords.put(keywordName, currentUserKeyword);
				}

			}

			youTubeDB.closeEntityManager(em);

		}

		logger.debug("setKeywordEvaluation ends..");

	}

	@Override
	public void setKeywordSelection(List<String> keywordList)
			throws YouTubeException {

		logger.debug("setKeywordSelection..");

		// checks whether the keywords exists on the BBDD and stores the new
		// keywords
		// checkKeywordExists(keywordsList);

		// keyword to check for the user
		String keywordName = null;
		// keyword counts for the user if exists
		Userkeyword currentUserKeyword = null;
		// visit count to update
		Integer currentVisitCount = null;

		if (keywordList != null) {

			// gets the EntityManager for the BBDD queries
			EntityManager em = youTubeDB.getEntityManager();

			// gets the iterator from the keyword list
			Iterator<String> keywordListIterator = keywordList.iterator();

			while (keywordListIterator.hasNext()) {

				// gets the current keyword
				keywordName = keywordListIterator.next();

				// if an entry exists in the model for this keyword..
				if (userKeywords.containsKey(keywordName)) {

					logger.debug("keyword entry exists");
					// get the userkeyword for this keyword..
					currentUserKeyword = userKeywords.get(keywordName);
					// and update the visitcount
					currentVisitCount = currentUserKeyword.getVisitcount();
					currentUserKeyword.setVisitcount(currentVisitCount + 1);

					// update the entry
					userKeywords.put(keywordName, currentUserKeyword);

					// if not, creates a new entry for the keyword..
				} else {

					Keyword keyword = new Keyword();
					// YoutubeUser youtubeUser = new YoutubeUser();

					// gets the Keyword object for this keyword from the BBDD
					keyword = youTubeDB.selectKeywordQuery(em,
							keywordName);

					// if the keyword not exist yet..
					if (keyword == null) {

						logger.debug("new keyword.. " + keywordName);
						// creates the new object with the keyword itself
						keyword = new Keyword(keywordName);
					}

					// creates the new UserKeyword object with keyword,
					// youtubeuser and sets the value of 'visitcount' to 1
					currentUserKeyword = new Userkeyword(0, 0, 1, keyword,
							userId);
					logger.debug("creates the entry for the keyword");

					// update the model for storing it on the BBDD later
					userKeywords.put(keywordName, currentUserKeyword);
					logger.debug("adds the keyword to the preference model");
				}

			}

			youTubeDB.closeEntityManager(em);

		}

		logger.debug("setKeywordSelection ends..");

	}		
}
